/*
 *
 */

/**
 *
 * @author vimi Administrator
 *  Dinh Ngoc 10.08.2010
 *
 */
import model.Course;
import model.Room;
import model.Curriculum;
import model.Pair;
import model.Triple;

import java.util.List;
import java.util.ArrayList;
import java.lang.String;

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Category;
import org.apache.log4j.PropertyConfigurator;
import java.util.Properties;
import java.io.IOException;


public class PernaltyCounter {
    static final Logger logger = Logger.getLogger(PernaltyCounter.class.getName());
    static final String LOG_PROPERTIES_FILE = "config/AppLog4J.properties";

    // input data
    public List<Course> CourseDomain;
    public List<Room> RoomDomain;
    public List<Curriculum> Curricula;

    public int DayNum;
    public int periods_per_day;
    public int [][] CP_R_Solution; // CP_R_Solution[c][p] = RoomIndex | NotSet
    

    public PernaltyCounter()
    {
        initializeLogger();
    }

    private void initializeLogger()
    {
        Properties logProperties = new Properties();

        try
        {
          // load our log4j properties / configuration file
          logProperties.load(PernaltyCounter.class.getResourceAsStream(LOG_PROPERTIES_FILE));
          PropertyConfigurator.configure(logProperties);
          logger.info("Logging initialized.");
        }
        catch(IOException e)
        {
          throw new RuntimeException("Unable to load logging property " + LOG_PROPERTIES_FILE);
        }
    }

    // Hard constraints
    public int AllLecturesOnTT()
    {
        int violations = 0;

        for (int c=0;c<CP_R_Solution.length;c++)
        {
            int CourseLectureNum = 0;

            for (int p=0;p<CP_R_Solution[0].length;p++)
            {
                if (CP_R_Solution[c][p]>-1) CourseLectureNum++;
            }

            Course cThCourse = CourseDomain.get(c);
            int diff = Math.abs(cThCourse.getLectureNum()-CourseLectureNum);
            logger.info(cThCourse.getCourseID()+" has "
                    +"LectNum"+cThCourse.getLectureNum()
                    +" and "+CourseLectureNum+" lectures were scheduled");
            if (diff>0) violations+= diff;

        }


        return violations;

    }

    public int RoomOccupancy()
    {
        int violations = 0;

        if (CP_R_Solution.length>0)
        {

        for (int p=0;p<CP_R_Solution[0].length;p++)
        {
            int [] violatedRoomArray= new int[RoomDomain.size()];
            // clear violatedRoomArray
            for (int r=0;r<violatedRoomArray.length;r++)
            {
                violatedRoomArray[r] = 0;
            }

            int CourseLectureNum = 0;

            for (int c=0;c<CP_R_Solution.length;c++)
            {
                if (CP_R_Solution[c][p]>-1)
                {
                    int r = CP_R_Solution[c][p];
                    violatedRoomArray[r]++;
                    if (violatedRoomArray[r]>1)
                    {

                        violations++;
                        Course cThCourse= CourseDomain.get(c);
                        logger.info(cThCourse.getCourseID()+" has same room");
                                    
                    }
                }
            }
        }

        }

        return violations;

    }

    public int sameTeacherLecturesConflict(int[][] Courses_waTeacher_Matrix)
    {
        int violations = 0;

        int [] CoursesAtPeriodArray= new int[CourseDomain.size()];
        int ArraylastIndex =-1;

        // clear violatedRoomArray
        for (int c=0;c<CoursesAtPeriodArray.length;c++)
        {
                CoursesAtPeriodArray[c] = 0;
        }



        if (CP_R_Solution.length>0)
        {

            for (int p=0;p<CP_R_Solution[0].length;p++)
            {
                ArraylastIndex = -1;
                for (int c=0;c<CP_R_Solution.length;c++)
                {
                    if (CP_R_Solution[c][p]>-1)
                    {
                        ArraylastIndex++;
                        if (ArraylastIndex<CoursesAtPeriodArray.length) CoursesAtPeriodArray[ArraylastIndex]=c;
                    }
                }
            }
        }

        for (int c1=0;c1<ArraylastIndex;c1++)
        {
            int c1Index = CoursesAtPeriodArray[c1];
            for (int c2=0;c2<ArraylastIndex;c2++)
            {
                int c2Index= CoursesAtPeriodArray[c2];

                if (Courses_waTeacher_Matrix[c1Index][c2Index]==1)
                {
                   //Course cThCourse= CourseDomain.get(c1Index);
                   logger.info(CourseDomain.get(c1Index).getCourseID()+
                           " has sameTeacherLecturesConflict with "+
                           CourseDomain.get(c2Index).getCourseID()+
                           " at "+c2);
                   violations++;
                }

            }
        }

        return violations*2;
    }
    public int sameTeacherLecturesConflict(int[][] Courses_waTeacher_Matrix,int[] Cs_sortedCs_Mapping)
    {
        int violations = 0;

        int [] CoursesAtPeriodArray= new int[CourseDomain.size()];
        int ArraylastIndex =-1;

        // clear violatedRoomArray
        for (int c=0;c<CoursesAtPeriodArray.length;c++)
        {
                CoursesAtPeriodArray[c] = 0;
        }



        if (CP_R_Solution.length>0)
        {

            for (int p=0;p<CP_R_Solution[0].length;p++)
            {
                ArraylastIndex = -1;
                for (int c=0;c<CP_R_Solution.length;c++)
                {
                    if (CP_R_Solution[c][p]>-1)
                    {
                        ArraylastIndex++;
                        if (ArraylastIndex<CoursesAtPeriodArray.length) CoursesAtPeriodArray[ArraylastIndex]=c;
                    }
                }
            }
        }

        for (int c1=0;c1<ArraylastIndex;c1++)
        {
            int c1Index = CoursesAtPeriodArray[c1];
            for (int c2=0;c2<ArraylastIndex;c2++)
            {
                int c2Index= CoursesAtPeriodArray[c2];

                if (Courses_waTeacher_Matrix[Cs_sortedCs_Mapping[c1Index]][Cs_sortedCs_Mapping[c2Index]]==1)
                {
                   //Course cThCourse= CourseDomain.get(c1Index);
                   logger.info(CourseDomain.get(c1Index).getCourseID()+
                           " has sameTeacherLecturesConflict with "+
                           CourseDomain.get(c2Index).getCourseID()+
                           " at "+c2);
                   violations++;
                }

            }
        }

        return violations*2;
    }

    public int sameCurriculumLecturesConflict(int[][] HardCurriculum_Matrix)
    {
        int violations = 0;

        int [] CoursesAtPeriodArray= new int[CourseDomain.size()];
        int ArraylastIndex =-1;
            // clear violatedRoomArray
        for (int c=0;c<CoursesAtPeriodArray.length;c++)
        {
                CoursesAtPeriodArray[c] = 0;
        }


        if (CP_R_Solution.length>0)
        {

            for (int p=0;p<CP_R_Solution[0].length;p++)
            {
                ArraylastIndex = -1;
                for (int c=0;c<CP_R_Solution.length;c++)
                {
                    if (CP_R_Solution[c][p]>-1)
                    {
                        ArraylastIndex++;
                        if (ArraylastIndex<CoursesAtPeriodArray.length)
                                CoursesAtPeriodArray[ArraylastIndex]=c;
                    }
                }
            }

        }

        //System.out.println("ArraylastIndex,CoursesAtPeriodArray.length"+ArraylastIndex+","+CoursesAtPeriodArray.length);

        for (int c1=0;c1<ArraylastIndex;c1++)
        {
            int c1Index = CoursesAtPeriodArray[c1];
            for (int c2=0;c2<ArraylastIndex;c2++)
            {
                int c2Index= CoursesAtPeriodArray[c2];

                if (HardCurriculum_Matrix[c1Index][c2Index]==1)
                {
                     logger.info(CourseDomain.get(c1Index).getCourseID()+
                           " has sameCurriculumLecturesConflict with "+
                           CourseDomain.get(c2Index).getCourseID()+
                           " at "+c2);
                    violations++;
                }
            }
        }

        return violations*2;
    }
    public int sameCurriculumLecturesConflict(int[][] HardCurriculum_Matrix,int[] Cs_sortedCs_Mapping)
    {
        int violations = 0;

        int [] CoursesAtPeriodArray= new int[CourseDomain.size()];
        int ArraylastIndex =-1;
            // clear violatedRoomArray
        for (int c=0;c<CoursesAtPeriodArray.length;c++)
        {
                CoursesAtPeriodArray[c] = 0;
        }


        if (CP_R_Solution.length>0)
        {

            for (int p=0;p<CP_R_Solution[0].length;p++)
            {
                ArraylastIndex = -1;
                for (int c=0;c<CP_R_Solution.length;c++)
                {
                    if (CP_R_Solution[c][p]>-1)
                    {
                        ArraylastIndex++;
                        if (ArraylastIndex<CoursesAtPeriodArray.length)
                                CoursesAtPeriodArray[ArraylastIndex]=c;
                    }
                }
            }

        }

        //System.out.println("ArraylastIndex,CoursesAtPeriodArray.length"+ArraylastIndex+","+CoursesAtPeriodArray.length);

        for (int c1=0;c1<ArraylastIndex;c1++)
        {
            int c1Index = CoursesAtPeriodArray[c1];
            for (int c2=0;c2<ArraylastIndex;c2++)
            {
                int c2Index= CoursesAtPeriodArray[c2];

                if (HardCurriculum_Matrix[Cs_sortedCs_Mapping[c1Index]][Cs_sortedCs_Mapping[c2Index]]==1)
                {
                     logger.info(CourseDomain.get(c1Index).getCourseID()+
                           " has sameCurriculumLecturesConflict with "+
                           CourseDomain.get(c2Index).getCourseID()+
                           " at "+c2);
                    violations++;
                }
            }
        }

        return violations*2;
    }

    public int TeacherAvailabilities(int[][] C_P_UnavailabilityMatrix)
    {
        int violations = 0;

        for (int c=0;c<CP_R_Solution.length;c++)
        {
            int CourseLectureNum = 0;

            for (int p=0;p<CP_R_Solution[0].length;p++)
            {
                if ((CP_R_Solution[c][p]>-1)&&(C_P_UnavailabilityMatrix[c][p]==1))
                {
                     logger.info(CourseDomain.get(c).getCourseID()+
                           " has TeacherAvailabilities "+
                           " at "+p);
                    violations++;
                }
            }

        }


        return violations;
    }

    public int TeacherAvailabilities(int[][] C_P_UnavailabilityMatrix,int[] Cs_sortedCs_Mapping)
    {
        int violations = 0;

        for (int c=0;c<CP_R_Solution.length;c++)
        {
            int CourseLectureNum = 0;

            for (int p=0;p<CP_R_Solution[0].length;p++)
            {
                if ((CP_R_Solution[c][p]>-1)&&(C_P_UnavailabilityMatrix[Cs_sortedCs_Mapping[c]][p]==1))
                {
                     logger.info(CourseDomain.get(c).getCourseID()+
                           " has TeacherAvailabilities conflict at "+p);
                    violations++;
                }
            }

        }


        return violations;
    }

    // Soft constraints
    public int RoomCapacity()
    {
        int violations = 0;

        for (int c=0;c<CP_R_Solution.length;c++)
        {
            for (int p=0;p<CP_R_Solution[0].length;p++)
            {
                if (CP_R_Solution[c][p]>-1)
                {
                   int r=CP_R_Solution[c][p];

                   Course cThCourse = CourseDomain.get(c);
                   Room givenRoom = RoomDomain.get(r);

                   int StuNumOver = cThCourse.getStudentNum()-givenRoom.getCapacity();
                   
                   if (StuNumOver>0)
                   {
                       logger.info(cThCourse.getCourseID()+
                           " has RoomCapacity conflicts with "+
                           givenRoom.getId()+
                           " -> "+cThCourse.getStudentNum()+","+givenRoom.getCapacity());
                       violations += StuNumOver;
                   }
                }
            }
        }

        return violations;

    }

    public int MinimumWorkingDays(int Periods_per_day)
    {
        int DaysbelowMinimum =0;

        for (int c=0;c<CP_R_Solution.length;c++)
        {
            Course cThCourse = CourseDomain.get(c);
            int MinimumWorkingDays = cThCourse.getMinWorkingDays();

            int actualWorkingDay = 0;
            
            int lastDay =-1;
            int actualDay = 0;

            for (int p=0;p<CP_R_Solution[0].length;p++)
            {
                actualDay= Math.round(p / Periods_per_day);
                if (actualDay!=lastDay)
                { 
                  if (CP_R_Solution[c][p]>-1)
                  {
                     actualWorkingDay++;
                     lastDay = actualDay;
                  }
                }
                
            }

            if (actualWorkingDay<MinimumWorkingDays)
            {

                DaysbelowMinimum += MinimumWorkingDays - actualWorkingDay;
                
                logger.info(cThCourse.getCourseID()+
                           " has MinimumWorkingDays conflicts "+DaysbelowMinimum);
            }

        }

        return DaysbelowMinimum*5;
    }

    public int CurriculumCompactness()
    {
        int isolatedLectures = 0;
        Curriculum.updateCIndex4Curricula(Curricula, CourseDomain);
        
        if (CP_R_Solution.length<=0 || Curricula.size()<=0) return -1;
        

        // 1. Calculate Array Curri_Periods_Lect
        
        int Curri_Periods_Lect[][]= new int[Curricula.size()][CP_R_Solution[0].length];
        // init array
        for (int i=0;i<Curri_Periods_Lect.length;i++)
        {
            for (int j=0;j<Curri_Periods_Lect[0].length;j++)
            {
                Curri_Periods_Lect[i][j]=0;
            }
        }


        // for each curriculum qi
        for (int i =0;i<=Curricula.size()-1;i++)
        {
           Curriculum IthCurriculum = Curricula.get(i);

           int IthCIndexList[]= new int[IthCurriculum.CIndexList.size()] ;

           // fill array IthCIndexList in
           for (int i_1 = 0;i_1 <= IthCurriculum.CIndexList.size()-1;i_1++)
           {
               //System.out.println("IthCurriculum.CIndexList.get(i_1)"+IthCurriculum.CIndexList.get(i_1));
               IthCIndexList[i_1]= Integer.parseInt(IthCurriculum.CIndexList.get(i_1));
               //System.out.println("IthCIndexList[i_1]"+IthCIndexList[i_1]);
           }

           for (int c=0;c<=IthCIndexList.length-1;c++)
           {
               int Cth_CourseIndex=IthCIndexList[c];
               // for each fill in Curri_Periods_Lect[i][]

               /*
               System.out.println("i, c, Cth_CourseIndex,Curricula.size():"
                       +i+","+c+","+Cth_CourseIndex+","+Curricula.size());
               */

               for (int t=0;t<=CP_R_Solution[Cth_CourseIndex].length-1;t++)
               {
                   if (CP_R_Solution[Cth_CourseIndex][t]!=-1) Curri_Periods_Lect[i][t]=1;
               }
           }
           
           // CurCourse
           

        }

        // 2. With filled array Curri_Periods_Lect count isolatedLectures for each
        /*
        System.out.println("Curri_Periods_Lect");
        for (int q=0;q<= Curri_Periods_Lect.length-1;q++)
        {
           System.out.print(Curricula.get(q).CurriculumID+":");
           for (int p=0;p<= Curri_Periods_Lect[0].length-1;p++)
           {
               System.out.print("Period "+p+"_"+Curri_Periods_Lect[q][p]+",");
           }
            System.out.println();
        }
        */

        for (int q=0;q<= Curri_Periods_Lect.length-1;q++)
        {
           int QthCurrPeriods[]=Curri_Periods_Lect[q];
           
           int day=0;

           while (day< DayNum)
           {
               int first_dayperiod=day*periods_per_day;
               int last_dayperiod=first_dayperiod+periods_per_day-1;

               while (QthCurrPeriods[first_dayperiod]==0 && first_dayperiod<last_dayperiod) first_dayperiod++;
               while (QthCurrPeriods[last_dayperiod]==0 && first_dayperiod<last_dayperiod) last_dayperiod--;

               for (int i=first_dayperiod;i<last_dayperiod;i++)
               {
                   if (QthCurrPeriods[i]==0)
                   {
                       isolatedLectures++;
                       logger.info(Curricula.get(q).CurriculumID+
                           " has isolated Lecture at "+i);
                   }
               }

              day++;
           } // end of while

           


        }

        return isolatedLectures;
    }

    public int RoomStability()
    {
        int distinctRooms = 0;

        for (int c=0;c<CP_R_Solution.length;c++)
        {
            int [] violatedRoomArray= new int[RoomDomain.size()];
            // clear violatedRoomArray
            int CdistinctRooms = 0;

            for (int r=0;r<violatedRoomArray.length;r++)
            {
                violatedRoomArray[r] = 0;
            }

            for (int p=0;p<CP_R_Solution[0].length;p++)
            {
                if (CP_R_Solution[c][p]>-1)
                {
                    int r = CP_R_Solution[c][p];
                    violatedRoomArray[r]++;
     
                }
            }

            for (int r=0;r<violatedRoomArray.length;r++)
            {
                   if (violatedRoomArray[r]!=0)
                   {
                       CdistinctRooms++;
                       
                   }
            }
            distinctRooms+= CdistinctRooms;

             logger.info(CourseDomain.get(c).getCourseID()+
                           " has RoomStability conflict "+CdistinctRooms+" distinct rooms.");


        }

        return distinctRooms; // distinctRooms-1
        
    }

     public void printSolMatrix()
    {
        System.out.println("CP_R_Solution");
        for (int i=0;i< CP_R_Solution.length;i++)
        {
            //System.out.print(_inputReader.Courses.get(i).getCourseID()+": ");

            for (int j=0;j< CP_R_Solution[i].length;j++)
            {
                System.out.print(CP_R_Solution[i][j]+" ");
            }

            System.out.println(" ");

        }
    }

}
